#pragma region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU Lesser General Public License.
/// </copyright>
#pragma endregion

#include "stdafx.h"
int counter = 0;
OSHProtocolHandler::OSHProtocolHandler() : _httpResponse(NULL), _locked(false)
{
	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] OSHProtocolHandler::OSHProtocolHandler - %d\r\n", this, counter);
	counter++;
}

OSHProtocolHandler::~OSHProtocolHandler()
{
	counter--;
	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] OSHProtocolHandler::~OSHProtocolHandler - %d\r\n", this, counter);

	// Warning! There is a problem (or bug?) in IE (or this code?) when a handler
	// is not properly released when the response content type is application/octet-stream.
	// In such cases the response data will be kept in memory (leaked).
	if(_httpResponse)
	{
		_httpResponse->Release();
		_httpResponse = NULL;
	}
}

void OSHProtocolHandler::Initialize(const wchar_t* basePath, OSHLibrary::IProtocolSink* protocolSink,
	IInternetProtocol* fileHelperProtocol, IInternetProtocol* httpHelperProtocol)
{
	_basePath = basePath;
	_protocolSink = protocolSink;

	_fileHelperProtocol = fileHelperProtocol;
	_fileHelperProtocolInfo = fileHelperProtocol;

	_httpHelperProtocol = httpHelperProtocol;
	_httpHelperProtocolInfo = httpHelperProtocol;
}

const char* OSHProtocolHandler::GetParseUrlAction(PARSEACTION action)
{
	const char* result = "UNKNOWN";
	if(action == PARSE_CANONICALIZE) result = "PARSE_CANONICALIZE";
	else if(action == PARSE_FRIENDLY) result = "PARSE_FRIENDLY";
	else if(action == PARSE_SECURITY_URL) result = "PARSE_SECURITY_URL";
	else if(action == PARSE_ROOTDOCUMENT) result = "PARSE_ROOTDOCUMENT";
	else if(action == PARSE_DOCUMENT) result = "PARSE_DOCUMENT";
	else if(action == PARSE_ANCHOR) result = "PARSE_ANCHOR";
	else if(action == PARSE_ENCODE) result = "PARSE_ENCODE";
	else if(action == PARSE_DECODE) result = "PARSE_DECODE";
	else if(action == PARSE_PATH_FROM_URL) result = "PARSE_PATH_FROM_URL";
	else if(action == PARSE_URL_FROM_PATH) result = "PARSE_URL_FROM_PATH";
	else if(action == PARSE_MIME) result = "PARSE_MIME";
	else if(action == PARSE_SERVER) result = "PARSE_SERVER";
	else if(action == PARSE_SCHEMA) result = "PARSE_SCHEMA";
	else if(action == PARSE_SITE) result = "PARSE_SITE";
	else if(action == PARSE_DOMAIN) result = "PARSE_DOMAIN";
	else if(action == PARSE_DOMAIN) result = "PARSE_DOMAIN";
	else if(action == PARSE_LOCATION) result = "PARSE_LOCATION";
	else if(action == PARSE_SECURITY_DOMAIN) result = "PARSE_SECURITY_DOMAIN";
	else if(action == PARSE_ESCAPE) result = "PARSE_ESCAPE";
	else if(action == PARSE_UNESCAPE) result = "PARSE_UNESCAPE";
	return result;
}

const char* OSHProtocolHandler::GetQueryName(QUERYOPTION queryOption)
{
	const char* result = "UNKNOWN";
	if(queryOption == QUERY_EXPIRATION_DATE) result = "QUERY_EXPIRATION_DATE";
	else if(queryOption == QUERY_TIME_OF_LAST_CHANGE) result = "QUERY_TIME_OF_LAST_CHANGE";
	else if(queryOption == QUERY_CONTENT_ENCODING) result = "QUERY_CONTENT_ENCODING";
	else if(queryOption == QUERY_CONTENT_TYPE) result = "QUERY_CONTENT_TYPE";
	else if(queryOption == QUERY_REFRESH) result = "QUERY_REFRESH";
	else if(queryOption == QUERY_RECOMBINE) result = "QUERY_RECOMBINE";
	else if(queryOption == QUERY_CAN_NAVIGATE) result = "QUERY_CAN_NAVIGATE";
	else if(queryOption == QUERY_USES_NETWORK) result = "QUERY_USES_NETWORK";
	else if(queryOption == QUERY_IS_CACHED) result = "QUERY_IS_CACHED";
	else if(queryOption == QUERY_IS_INSTALLEDENTRY) result = "QUERY_IS_INSTALLEDENTRY";
	else if(queryOption == QUERY_IS_CACHED_OR_MAPPED) result = "QUERY_IS_CACHED_OR_MAPPED";
	else if(queryOption == QUERY_USES_CACHE) result = "QUERY_USES_CACHE";
	else if(queryOption == QUERY_IS_SECURE) result = "QUERY_IS_SECURE";
	else if(queryOption == QUERY_IS_SAFE) result = "QUERY_IS_SAFE";
	return result;
}

bool OSHProtocolHandler::IsOSHNamespace(LPCWSTR szUrl)
{
	size_t urlLength = wcslen(szUrl);
	if(urlLength < OSHNameSpaceAndHostSize)
		return false;

	if(_wcsnicmp(szUrl, OSHNameSpaceAndHost, OSHNameSpaceAndHostSize) != 0)
		return false;

	if(urlLength > OSHNameSpaceAndHostSize && szUrl[OSHNameSpaceAndHostSize] != '/')
		return false;

	return true;
}

// Parses canonicalized OSH url and returns a helper protocol's specific url.
// E.g.:
//   "osh://localhost/index.html"  ->  "file:///%OSHFolder%/index.html"
//   "osh://localhost/http://www.microsoft.com/" -> "http://www.microsoft.com/"
//   "osh://localhost/file:///C:\index.html" -> "file:///C:\index.html"
//
// For security reason "osh://localhost/file:" and "osh://localhost/http:" protocols
// are no longer supported. There is a better way to access iframe's content.
bool OSHProtocolHandler::ParseURL(LPCWSTR szUrl, CComBSTR& resultUrl,
	CComPtr<IInternetProtocol>& helperProtocol, CComPtr<IInternetProtocolInfo>& helperProtocolInfo)
{
	size_t urlLength = wcslen(szUrl);
	if(urlLength < OSHNameSpaceAndHostSize + 1 + 1)	// + size of "/" char after OSH host name + some other char
		return false;

	if(wcsncmp(szUrl, OSHNameSpaceAndHost, OSHNameSpaceAndHostSize) != 0 ||
		szUrl[OSHNameSpaceAndHostSize] != '/')
		return false;

	if(resultUrl != NULL)
		resultUrl.Empty();

	//if(IsCustomProtocol(szUrl + OSHNameSpaceAndHostSize + 1, HttpNameSpace, HttpNameSpaceSize))
	//{
	//	if(_httpHelperProtocol == NULL)
	//		return false;

	//	helperProtocol = _httpHelperProtocol;
	//	helperProtocolInfo = _httpHelperProtocolInfo;

	//	resultUrl.Append(szUrl + OSHNameSpaceAndHostSize + 1);   // skip "/" char after OSH host name
	//}
	//else if(IsCustomProtocol(szUrl + OSHNameSpaceAndHostSize + 1, FileNameSpace, FileNameSpaceSize))
	//{
	//	if(_fileHelperProtocol == NULL)
	//		return false;

	//	helperProtocol = _fileHelperProtocol;
	//	helperProtocolInfo = _fileHelperProtocolInfo;

	//	resultUrl.Append(szUrl + OSHNameSpaceAndHostSize + 1);   // skip "/" char after OSH host name
	//}
	//else
	if(_basePath != NULL && _fileHelperProtocol != NULL)
	{
		helperProtocol = _fileHelperProtocol;
		helperProtocolInfo = _fileHelperProtocolInfo;

		resultUrl.Append(FileUrlPrefix);
		resultUrl.Append(_basePath);
		resultUrl.Append(szUrl + OSHNameSpaceAndHostSize);   // "/" char after OSH host name is included
	}
	else
	{
		helperProtocol = NULL;
		helperProtocolInfo = NULL;
		resultUrl.Append(szUrl);
	}

	return true;
}

bool OSHProtocolHandler::IsCustomProtocol(const wchar_t* url, const wchar_t* protocol, size_t protocolLength)
{
	size_t urlLength = wcslen(url);
	if(urlLength < protocolLength + 1 + 1)	// SchemaName + ":" + some other char
		return false;

	if(wcsncmp(url, protocol, protocolLength) == 0 && url[protocolLength] == ':')
		return true;

	return false;
}

//bool OSHProtocolHandler::CheckAccessRights(const wchar_t* fileUrl, CString& filePath)
//{
//	wchar_t buffer[MAX_PATH] = {0};
//	DWORD pathLength = 0;
//	HRESULT hr = CoInternetParseUrl(fileUrl, PARSE_PATH_FROM_URL, 0, buffer, SIZEOF_ARRAY(buffer),
//		&pathLength, 0);
//	if(FAILED(hr))
//		return false;

//	WIN32_FIND_DATA findFileData;
//	HANDLE hFind = ::FindFirstFileEx(buffer, FindExInfoStandard, &findFileData,
//		FindExSearchNameMatch, NULL, 0);
//	if(hFind == INVALID_HANDLE_VALUE)
//		return false;

//	//GetFilePathFromHandle();

//	FindClose(hFind);

//	filePath = findFileData.cFileName;

//	if(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
//		return false;

//	size_t filePathLength = filePath.GetLength();
//	size_t basePathLength = wcslen(_basePath);
//	if(filePathLength < basePathLength)
//		return false;

//	if(wcsnicmp(_basePath, filePath.GetBuffer(), basePathLength) != 0)
//		return false;

//	return true;
//}


HRESULT OSHProtocolHandler::GetHttpNegotiate(
	IInternetProtocolSink* inetProtocolSink, IHttpNegotiate** httpNegotiate)
{
	if(inetProtocolSink == NULL) return E_INVALIDARG;
	if(httpNegotiate == NULL) return E_POINTER;
	*httpNegotiate = NULL;

	CComPtr<IServiceProvider> inetProtocolServices;
	HRESULT hr = inetProtocolSink->QueryInterface(&inetProtocolServices);
	if(FAILED(hr)) return hr;

	hr = inetProtocolServices->QueryService(
		__uuidof(IHttpNegotiate), __uuidof(IHttpNegotiate), reinterpret_cast<void**>(httpNegotiate));
	return hr;
}


HRESULT OSHProtocolHandler::StartEx(LPCWSTR url, IInternetProtocolSink* inetProtocolSink,
	IInternetBindInfo* inetBindInfo, DWORD grfPI)
{
	if(_httpResponse)
	{
		_httpResponse->Release();
		_httpResponse = NULL;
	}

	CComPtr<IHttpNegotiate> httpNegotiate;
	HRESULT hr = GetHttpNegotiate(inetProtocolSink, &httpNegotiate);
	if(FAILED(hr)) return hr;

	HttpRequest* httpRequest = NULL;
	hr = CreateHttpRequest(url, inetBindInfo, &httpRequest);
	if(FAILED(hr)) return hr;

	do
	{
		// Get initial headers
		LPWSTR initialRequestHeaders;
		hr = httpNegotiate->BeginningTransaction(url, NULL, 0, &initialRequestHeaders);
		if(FAILED(hr)) break;
		if(initialRequestHeaders != NULL)
		{
			hr = httpRequest->GetHeaders().AddHeaders(initialRequestHeaders);
			if(FAILED(hr)) break;
		}

		// Process request
		CString requestHeaders = httpRequest->GetHeaders().ToString();
		DWORD statusCode = 0;
		CComBSTR redirectUri;
		CComBSTR statusDescription;
		CComBSTR usedRequestHeaders;
		CComBSTR responseHeaders;
		CComSafeArray<BYTE> responseContent;
		CComBSTR cacheFilePath;
		hr = _protocolSink->ProcessRequest(
			const_cast<LPWSTR>(httpRequest->GetMethod()),
			const_cast<LPWSTR>(url),
			const_cast<LPWSTR>(requestHeaders.GetString()),
			httpRequest->GetContent(),
			&redirectUri,
			&usedRequestHeaders,
			&statusCode,
			&statusDescription, 
			&responseHeaders,
			responseContent.GetSafeArrayPtr(),
			&cacheFilePath);

		ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
			"[%p] OSHProtocolHandler::StartEx(%S, %S => %S) : 0x%08X\r\n"
			"--- Cache file path: %S\r\n"
			"--- Request headers:\r\n%S\r\n"
			"--- Response headers:\r\nHTTP/1.1 %d %S\r\n%S\r\n",
			this,
			httpRequest->GetMethod(), url, redirectUri, hr,
			cacheFilePath,
			usedRequestHeaders,
			statusCode, statusDescription,
			responseHeaders);

		if(FAILED(hr)) break;



		// Save response
		_httpResponse = new HttpResponse(httpRequest);
		if(_httpResponse == NULL)
		{
			hr = E_OUTOFMEMORY;
			break;
		}

		_httpResponse->SetStatusLine(statusCode, statusDescription);
		_httpResponse->GetHeaders().AddHeaders(responseHeaders);
		if(responseContent != NULL)
			_httpResponse->AttachContent(responseContent.Detach());

		if(redirectUri != NULL)
		{
			hr = inetProtocolSink->ReportProgress(BINDSTATUS_REDIRECTING, redirectUri);
			if(FAILED(hr)) break;
		}

		LPWSTR addRequestHeaders = NULL;
		//CString responseHeaders(_httpResponse->GetHeaders().ToString());
		hr = httpNegotiate->OnResponse(_httpResponse->GetStatusCode(),
			responseHeaders, usedRequestHeaders, &addRequestHeaders);
		ATLASSERT(addRequestHeaders == NULL);
		if(FAILED(hr)) break;

		CString mimeType;
		if(_httpResponse->GetHeaders().GetMimeType(mimeType))
		{
			hr = inetProtocolSink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, mimeType);
			if(FAILED(hr)) break;
		}

		if(cacheFilePath != NULL)
		{
			hr = inetProtocolSink->ReportProgress(BINDSTATUS_CACHEFILENAMEAVAILABLE, cacheFilePath);
			if(FAILED(hr)) break;
		}

		hr = inetProtocolSink->ReportData(BSCF_LASTDATANOTIFICATION,
			_httpResponse->GetContentSize(), _httpResponse->GetContentSize());
		if(FAILED(hr)) break;

		hr = inetProtocolSink->ReportResult(S_OK, 0, NULL);
		if(FAILED(hr)) break;
	}
	while(!this);

	httpRequest->Release();

	return hr;
}

HRESULT OSHProtocolHandler::CreateHttpRequest(
	LPCWSTR url, IInternetBindInfo* inetBindInfo, HttpRequest** httpRequest)
{
	if(url == NULL) return E_INVALIDARG;
	if(inetBindInfo == NULL) return E_INVALIDARG;
	if(httpRequest == NULL) return E_POINTER;
	*httpRequest = NULL;

	DWORD bindFlags;
	BINDINFO bindInfo = {0};
	bindInfo.cbSize = sizeof(bindInfo);
	HRESULT hr = inetBindInfo->GetBindInfo(&bindFlags, &bindInfo);
	if(FAILED(hr)) return hr;

	if(bindInfo.dwBindVerb == BINDVERB_POST)
	{
		CComSafeArray<BYTE> postData;
		CComBSTR postDataMime;

		if(bindInfo.stgmedData.tymed != TYMED_HGLOBAL)
			return E_INVALIDARG;

		// Copy post data
		if(bindInfo.cbstgmedData > 0)
		{
			DWORD postDataSize = bindInfo.cbstgmedData;

			void* mem = ::GlobalLock(bindInfo.stgmedData.hGlobal);
			if(mem == NULL) return AtlHresultFromLastError();

			hr = postData.Add(postDataSize, reinterpret_cast<BYTE*>(mem));
			//hr = postData.AppendBytes(reinterpret_cast<char*>(mem), postDataSize);
			if(FAILED(hr)) return hr;

			::GlobalUnlock(bindInfo.stgmedData.hGlobal);
		}

		// Copy post data mime
		LPOLESTR mimeType = NULL;
		ULONG mimeTypeSize;
		hr = inetBindInfo->GetBindString(BINDSTRING_POST_DATA_MIME, &mimeType, 1, &mimeTypeSize);
		if(hr == S_OK && mimeType != NULL)
		{
			hr = postDataMime.Append(mimeType);
			::CoTaskMemFree(mimeType);

			if(FAILED(hr)) return hr;
		}
		else
			postDataMime.Append("application/x-www-form-urlencoded");

		*httpRequest = new HttpRequest(L"POST", url);
		if(postData != NULL)
			(*httpRequest)->AttachContent(postData.Detach(), postDataMime);
	}
	else if(bindInfo.dwBindVerb == BINDVERB_GET)
	{
		*httpRequest = new HttpRequest(L"GET", url);
	}
	else
		return E_INVALIDARG;

	if(*httpRequest == NULL)
		return E_OUTOFMEMORY;

	return hr;
}


//*********************************************************
// IInternetProtocolInfo
//*********************************************************
#pragma region IInternetProtocolInfo
STDMETHODIMP OSHProtocolHandler::ParseUrl(/* [in] */ LPCWSTR pwzUrl, /* [in] */ PARSEACTION parseAction,
	/* [in] */ DWORD dwParseFlags, /* [out] */ LPWSTR pwzResult,
	/* [in] */ DWORD cchResult, /* [out] */ DWORD* pcchResult, /* [in] */ DWORD dwReserved)
{
	HRESULT hr = INET_E_DEFAULT_ACTION;
	//if(parseAction == PARSE_CANONICALIZE)
	//{
	//	CComBSTR canonicalizedUrl;
	//	canonicalizedUrl.Append(OSHNameSpaceAndHost);

	//	const wchar_t* pathStart = pwzUrl + OSHNameSpaceSize + 1;
	//	if(wcslen(pwzUrl) >= OSHNameSpaceAndHostSize &&
	//		wcsnicmp(pwzUrl, OSHNameSpaceAndHost, OSHNameSpaceAndHostSize) == 0)
	//		pathStart = pwzUrl + OSHNameSpaceAndHostSize;

	//	canonicalizedUrl.Append(pathStart);

	//	*pcchResult = canonicalizedUrl.Length() + 1;
	//	if(cchResult < *pcchResult)
	//		hr = S_FALSE;
	//	else
	//	{
	//		wcscpy(pwzResult, canonicalizedUrl);
	//		hr = S_OK;
	//	}
	//}
	//else if(parseAction == PARSE_PATH_FROM_URL)
	//{
	//	CString url(pwzUrl);
	//	if(url.GetLength() >= (OSHNameSpaceAndHostSize + 1) &&
	//		url.Left(OSHNameSpaceAndHostSize).CompareNoCase(OSHNameSpaceAndHost) == 0 &&
	//		url.GetAt(OSHNameSpaceAndHostSize) == '/')
	//	{
	//		*pcchResult = static_cast<DWORD>(url.GetLength() - OSHNameSpaceAndHostSize + 1);
	//		if(cchResult < *pcchResult)
	//			hr = S_FALSE;
	//		else
	//		{
	//			wcscpy(pwzResult, pwzUrl + OSHNameSpaceAndHostSize);
	//			hr = S_OK;
	//		}
	//	}
	//}
	if(parseAction == PARSE_SECURITY_URL)
	{
		*pcchResult = static_cast<DWORD>(wcslen(OSHSecurityURL) + 1);
		if(cchResult < *pcchResult)
			hr = S_FALSE;
		else
		{
			wcscpy_s(pwzResult, cchResult, OSHSecurityURL);
			hr = S_OK;
		}
	}
	else if(parseAction == PARSE_SECURITY_DOMAIN)
	{
		*pcchResult = static_cast<DWORD>(wcslen(OSHSecurityDomain) + 1);
		if(cchResult < *pcchResult)
			hr = S_FALSE;
		else
		{
			wcscpy_s(pwzResult, cchResult, OSHSecurityDomain);
			hr = S_OK;
		}
	}

	if(hr == S_OK)
		ATLTRACE_TS(atlTraceProtocolInfo, TraceLevel::Verbose,
			"[%p] IInternetProtocolInfo::ParseUrl(\"%S\", %s, 0x%X) = \"%S\" : 0x%X\r\n",
			this, pwzUrl, GetParseUrlAction(parseAction), cchResult, pwzResult, hr);
	else
		ATLTRACE_TS(atlTraceProtocolInfo, TraceLevel::Verbose,
			"[%p] IInternetProtocolInfo::ParseUrl(\"%S\", %s, 0x%X) : 0x%X\r\n",
			this, pwzUrl, GetParseUrlAction(parseAction), cchResult, hr);

	return hr;

	//ATLASSERT(_helperProtocolInfo != NULL);
	//if(_helperProtocolInfo == NULL)
	//	return E_UNEXPECTED;

	//return _helperProtocolInfo->ParseUrl(pwzUrl, parseAction, dwParseFlags,
	//	pwzResult, cchResult, pcchResult, dwReserved);
}

STDMETHODIMP OSHProtocolHandler::CombineUrl(/* [in] */ LPCWSTR pwzBaseUrl, /* [in] */ LPCWSTR pwzRelativeUrl,
	/* [in] */ DWORD dwCombineFlags, /* [out] */ LPWSTR pwzResult,
	/* [in] */ DWORD cchResult, /* [out] */ DWORD* pcchResult, /* [in] */ DWORD dwReserved)
{
	if(pcchResult == NULL) return E_POINTER;

	HRESULT hr = INET_E_DEFAULT_ACTION;

	if(IsOSHNamespace(pwzRelativeUrl))
	{
		// It's absolute url
		*pcchResult = (DWORD)wcslen(pwzRelativeUrl);
		if(cchResult < *pcchResult)
			hr = S_FALSE;
		else
		{
			wcscpy_s(pwzResult, cchResult, pwzRelativeUrl);
			hr = S_OK;
		}
	}
	else
	{
		CComBSTR helperProtocolUrl;
		CComPtr<IInternetProtocol> helperProtocol;
		CComPtr<IInternetProtocolInfo> helperProtocolInfo;
		if(ParseURL(pwzBaseUrl, helperProtocolUrl, helperProtocol, helperProtocolInfo) &&
			helperProtocol == _httpHelperProtocol)
		{
			CString resultUrl;
			resultUrl.Preallocate(cchResult);
			DWORD actualResultLength;
			hr = CoInternetCombineUrl(helperProtocolUrl, pwzRelativeUrl,
				dwCombineFlags, resultUrl.GetBuffer(), cchResult,
				&actualResultLength, dwReserved);
			if(SUCCEEDED(hr))
			{
				CString combinetUrl;
				combinetUrl = OSHNameSpaceAndHost;
				combinetUrl.AppendChar('/');
				combinetUrl.Append(resultUrl.GetBuffer());

				*pcchResult = combinetUrl.GetLength() + 1;
				if(cchResult < *pcchResult)
					hr = S_FALSE;
				else
				{
					wcsncpy_s(pwzResult, cchResult, combinetUrl.GetBuffer(), combinetUrl.GetLength());
					pwzResult[combinetUrl.GetLength()] = '\0';
				}
			}
		}
	}
	ATLTRACE_TS(atlTraceProtocolInfo, TraceLevel::Verbose, "[%p] IInternetProtocolInfo::CombineUrl(\"%S\", ", this, pwzBaseUrl);
	ATLTRACE(atlTraceProtocolInfo, TraceLevel::Verbose, "\"%S\", 0x%X, %d, %d) ",
		pwzRelativeUrl, dwCombineFlags, cchResult, *pcchResult);
	if(hr == INET_E_DEFAULT_ACTION)
	{
		ATLTRACE(atlTraceProtocolInfo, TraceLevel::Verbose, ": INET_E_DEFAULT_ACTION\r\n");
	}
	else
	{
		ATLTRACE(atlTraceProtocolInfo, TraceLevel::Verbose, "= \"%S\" : 0x%X\r\n", pwzResult, hr);
	}

	return hr;

	//ATLASSERT(_helperProtocolInfo != NULL);
	//if(_helperProtocolInfo == NULL)
	//	return E_UNEXPECTED;

	//return _helperProtocolInfo->CombineUrl(pwzBaseUrl, pwzRelativeUrl, dwCombineFlags,
	//	pwzResult, cchResult, pcchResult, dwReserved);
}

STDMETHODIMP OSHProtocolHandler::CompareUrl(
	/* [in] */ LPCWSTR pwzUrl1, /* [in] */ LPCWSTR pwzUrl2, /* [in] */ DWORD dwCompareFlags)
{
	ATLTRACE_TS(atlTraceProtocolInfo, TraceLevel::Verbose, "[%p] IInternetProtocolInfo::CompareUrl(\"%S\"", this, pwzUrl1);
	ATLTRACE(atlTraceProtocolInfo, TraceLevel::Verbose, "\"%S\", 0x%X)\r\n", pwzUrl2, dwCompareFlags);

	return INET_E_DEFAULT_ACTION;

	//ATLASSERT(_helperProtocolInfo != NULL);
	//if(_helperProtocolInfo == NULL)
	//	return E_UNEXPECTED;

	//return _helperProtocolInfo->CompareUrl(pwzUrl1, pwzUrl2, dwCompareFlags);
}

STDMETHODIMP OSHProtocolHandler::QueryInfo(/* [in] */ LPCWSTR pwzUrl, /* [in] */ QUERYOPTION queryOption,
	/* [in] */ DWORD dwQueryFlags, /* [size_is][out][in] */ LPVOID pBuffer,
	/* [in] */ DWORD cbBuffer, /* [out][in] */ DWORD* pcbBuf, /* [in] */ DWORD dwReserved)
{
	ATLTRACE_TS(atlTraceProtocolInfo, TraceLevel::Verbose,
		"[%p] IInternetProtocolInfo::QueryInfo(\"%S\", %s)\r\n", this, pwzUrl, GetQueryName(queryOption));

	if(queryOption == QUERY_IS_SECURE ||
		queryOption == QUERY_IS_SAFE ||
		queryOption == QUERY_USES_NETWORK)
	{
		*pcbBuf = 4;
		if(cbBuffer < *pcbBuf) return S_FALSE;
		*(DWORD*)pBuffer = 0;
		return S_OK;
	}
	else if(queryOption == QUERY_CAN_NAVIGATE)
	{
		*pcbBuf = 4;
		if(cbBuffer < *pcbBuf) return S_FALSE;
		*(DWORD*)pBuffer = 1;
		return S_OK;
	}

	return INET_E_QUERYOPTION_UNKNOWN;

	//ATLASSERT(_helperProtocolInfo != NULL);
	//if(_helperProtocolInfo == NULL)
	//	return E_UNEXPECTED;

	//return _helperProtocolInfo->QueryInfo(pwzUrl, queryOption, dwQueryFlags,
	//	pBuffer, cbBuffer, pcbBuf, dwReserved);
}
#pragma endregion


//*********************************************************
// IInternetProtocolRoot
//*********************************************************
#pragma region IInternetProtocolRoot
STDMETHODIMP OSHProtocolHandler::Start(LPCWSTR szUrl, IInternetProtocolSink* pOIProtSink,
	IInternetBindInfo* pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved)
{
	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Info,
		"[%p] IInternetProtocolRoot::Start(\"%S\", 0x%X)\r\n", this, szUrl, grfPI);

	_helperProtocol = NULL;
	_helperProtocolInfo = reinterpret_cast<IUnknown*>(NULL);

	_usingHelperProtocol = true;
	CComBSTR targetUrl;
	if(!ParseURL(szUrl, targetUrl, _helperProtocol, _helperProtocolInfo))
		return E_FAIL;

	HRESULT hr = S_OK;
	if(IsOSHNamespace(targetUrl) && (_basePath == NULL || _helperProtocol == NULL))
	{
		_usingHelperProtocol = false; // First of all set protocol. Will be used in Terminate() method

		if(_protocolSink == NULL)
			return E_FAIL;

		// Warning! Holding pOIProtSink pointer will prevent protocol from destruction
		hr = StartEx(szUrl, pOIProtSink, pOIBindInfo, grfPI);
	}
	else
	{
		if(pOIProtSink != NULL || pOIBindInfo != NULL)
		{
			CComObject<OSHProtocolHandlerSink>* sink = NULL;
			hr = CComObject<OSHProtocolHandlerSink>::CreateInstance(&sink);
			ATLASSERT(SUCCEEDED(hr));
			if(FAILED(hr)) return E_FAIL;

			sink->SetInterfaces(pOIProtSink, pOIBindInfo);

			CComPtr<IInternetProtocolSink> protSink(sink);
			CComPtr<IInternetBindInfo> bindInfo(sink);

			if(pOIProtSink != NULL)
				pOIProtSink = protSink;

			if(pOIBindInfo != NULL)
				pOIBindInfo = bindInfo;

			// This code has to be called in this code block to preserve sink object from
			// destruction during destruction of CComQIPtr's.
			// If _helperProtocol AddRefs sink object, then it will be deleted during
			// destruction of _helperProtocol and we can forget about it.
			hr = _helperProtocol->Start(targetUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved);
		}
		else
			hr = _helperProtocol->Start(targetUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved);
	}

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocolRoot::Start(\"%S\") : 0x%X\r\n", this, targetUrl, hr);

	return hr;
}

STDMETHODIMP OSHProtocolHandler::Continue(PROTOCOLDATA* pProtocolData)
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL) return E_UNEXPECTED;

		hr = _helperProtocol->Continue(pProtocolData);
	}	
	
	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocolRoot::Continue(0x%08X) : 0x%08X\r\n", this, pProtocolData, hr);

	return hr;
}

STDMETHODIMP OSHProtocolHandler::Abort(HRESULT hrReason, DWORD dwOptions)
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL) return E_UNEXPECTED;

		hr = _helperProtocol->Abort(hrReason, dwOptions);
	}

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocolRoot::Abort(reason=0x%08X, options=0x%08X) : 0x%08X\r\n", this, hrReason, dwOptions, hr);

	return hr;
}

STDMETHODIMP OSHProtocolHandler::Terminate(DWORD dwOptions)
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL) return E_UNEXPECTED;

		hr = _helperProtocol->Terminate(dwOptions);
	}

	//if(_httpResponse && !_locked)
	//{
	//	_httpResponse->Release();
	//	_httpResponse = NULL;
	//}

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocolRoot::Terminate(options=0x%X) : 0x%08X\r\n", this, dwOptions, hr);

	return hr;
}

STDMETHODIMP OSHProtocolHandler::Suspend()
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL) return E_UNEXPECTED;

		hr = _helperProtocol->Suspend();
	}

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocolRoot::Suspend() : 0x%08X\r\n", this, hr);

	return hr;
}

STDMETHODIMP OSHProtocolHandler::Resume()
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL)
			return E_UNEXPECTED;

		hr = _helperProtocol->Resume();
	}

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocolRoot::Resume() : 0x%08X\r\n", this, hr);

	return hr;
}
#pragma endregion


//*********************************************************
// IInternetProtocol
//*********************************************************
#pragma region IInternetProtocol
STDMETHODIMP OSHProtocolHandler::Read(void* pv, ULONG cb, ULONG* pcbRead)
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL)
			hr = E_UNEXPECTED;
		else
			hr = _helperProtocol->Read(pv, cb, pcbRead);
	}
	else
	{
		if(pv == NULL)
			hr = E_POINTER;
		else
		{
			if(pcbRead == NULL)
				hr = E_POINTER;
			else
			{
				*pcbRead = 0;
				if(_httpResponse == NULL)
					hr = E_UNEXPECTED;
				else
				{
					*pcbRead = _httpResponse->Read(reinterpret_cast<BYTE*>(pv), cb);
					if(*pcbRead == 0) hr = S_FALSE;
				}
			}
		}
	}

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocol::Read(count=%d, read=%d) : 0x%08X\r\n", this, cb, (pcbRead ? *pcbRead : 0), hr);

	return hr;
}

STDMETHODIMP OSHProtocolHandler::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL) return E_UNEXPECTED;

		hr = _helperProtocol->Seek(dlibMove, dwOrigin, plibNewPosition);
	}
	else
		return E_UNEXPECTED;

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocol::Seek(offset=%d, origin=%d) : 0x%08X\r\n", this, dlibMove.LowPart, dwOrigin, hr);

	return hr;
}

STDMETHODIMP OSHProtocolHandler::LockRequest(DWORD dwOptions)
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL) return E_UNEXPECTED;

		hr = _helperProtocol->LockRequest(dwOptions);
	}

	_locked = true;

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocol::LockRequest(options=%d) : 0x%08X\r\n", this, dwOptions, hr);

	return hr;
}

STDMETHODIMP OSHProtocolHandler::UnlockRequest()
{
	HRESULT hr = S_OK;
	if(_usingHelperProtocol)
	{
		ATLASSERT(_helperProtocol != NULL);
		if(_helperProtocol == NULL) return E_UNEXPECTED;

		hr = _helperProtocol->UnlockRequest();
	}

	_locked = false;
	//if(_httpResponse)
	//{
	//	_httpResponse->Release();
	//	_httpResponse = NULL;
	//}

	ATLTRACE_TS(atlTraceProtocol, TraceLevel::Verbose,
		"[%p] IInternetProtocol::UnlockRequest() : 0x%08X\r\n", this, hr);

	return hr;
}
#pragma endregion
